package com.hup.utils.swing.customView;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.util.Arrays;
import java.util.function.Function;

/**
 * @author hugan
 * @date 2021/5/24
 */
@Log4j2
public class SeekBarView<T> extends JPanel implements ChangeListener {

    private JLabel txtLabel;
    private JLabel txtValue;
    private JSlider slider;
    //属性
    @Getter
    private String label = "label:";
    @Getter
    private ValueType valueType = ValueType.INTEGER;
    /**
     * 单位
     */
    @Getter
    private String unit = null;
    /**
     * 取值字符串,格式"1,2,10,20"
     */
    @Getter
    private String valuesStr = "0";
    /**
     * 取值,数据类型:{@link ValueType}
     */
    private Object[] values;
    /**
     * 当前值的index,等于 {@link JSlider#getValue()}
     */
    @Getter
    private int valueIndex = 0;
    /**
     * SeekBar值监听
     */
    @Setter
    private ValueChangedListener<T> valueChangedListener;

    public SeekBarView() {
        initLayout();
        setValuesStr(valuesStr);
        slider.addChangeListener(this);
    }

    private void initLayout() {
        setLayout(new GridBagLayout());
        txtLabel = new JLabel(label);
        GridBagConstraints gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        add(txtLabel, gbc);
        txtValue = new JLabel();
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 0;
        gbc.anchor = GridBagConstraints.WEST;
        add(txtValue, gbc);
        slider = new JSlider();
        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(slider, gbc);
    }

    @Override
    public void stateChanged(ChangeEvent e) {
        int value = slider.getValue();
        boolean fromUser = slider.getValueIsAdjusting();
        log.debug("value={}, Adjusting={}", value, fromUser);
        valueIndex = value;
        updateValueText();
        if (valueChangedListener != null) valueChangedListener.onValueChanged(getValue(), fromUser);
    }

    /**
     * 按[values+unit的最宽字符串]固定txtValue的宽度;
     * 解决SeekBar拖拽时,因为txtValue文本更新导致布局鬼畜的问题
     */
    private void fixValueTextWidth(String[] split) {
        String longest = split[0];
        for (String value : split) {
            if (value.length() > longest.length()) longest = value;
        }
        if (unit != null) longest += unit;

        FontMetrics metrics = txtValue.getFontMetrics(txtValue.getFont());
        float width = metrics.stringWidth(longest) + 1;
        Dimension viewSize = new Dimension((int) width, metrics.getHeight());
        //log.info("viewSize={}, longest={}", viewSize, longest);
        txtValue.setMinimumSize(viewSize);
        txtValue.setMaximumSize(viewSize);
        txtValue.setPreferredSize(viewSize);
        txtValue.setSize(viewSize);
    }

    /**
     * 更新[txtValue]的文本
     */
    private void updateValueText() {
        if (unit == null) {
            txtValue.setText(values[valueIndex].toString());
        } else {
            txtValue.setText(String.format("%s%s", values[valueIndex], unit));
        }
    }

    public void setLabel(String label) {
        this.label = label;
        txtLabel.setText(label);
    }

    public void setValueType(ValueType valueType) {
        this.valueType = valueType;
        setValuesStr(valuesStr);
    }

    public void setUnit(String unit) {
        this.unit = unit;
        setValuesStr(valuesStr);
    }

    public void setValuesStr(String valuesStr) {
        if (valuesStr == null) {
            log.error("入参不能为null");
            return;
        }
        this.valuesStr = valuesStr;
        String[] split = valuesStr.split(",");
        values = new Object[split.length];
        for (int i = values.length - 1; i >= 0; i--) {
            values[i] = valueType.parser.apply(split[i]);
        }
        slider.setMaximum(values.length - 1);
        valueIndex = Math.min(valueIndex, values.length - 1);

        fixValueTextWidth(split);
        updateValueText();
    }

    @SuppressWarnings("unchecked")
    public void setValueIndex(int valueIndex) {
        this.valueIndex = valueIndex;
        setValue((T) values[valueIndex]);
    }

    @SuppressWarnings("unchecked")
    public T getValue() {
        return (T) values[valueIndex];
    }

    public void setValue(T value) {
        log.debug("value={}, label={}", value, label);
        for (int i = 0, valuesLength = values.length; i < valuesLength; i++) {
            Object o = values[i];
            if (values[i].equals(value)) {
                slider.setValue(i);//由SB监听触发更新UI和valueIndex
                return;
            }
        }
        log.error("value异常:{}, values={}", value, Arrays.toString(values));
    }

    /**
     * 数据类型
     */
    public enum ValueType {
        INTEGER(0, Integer::parseInt),
        FLOAT(1, Float::parseFloat),
        DOUBLE(2, Double::parseDouble),
        STRING(3, s -> s);

        private final int value;
        private final Function<String, Object> parser;

        ValueType(int value, Function<String, Object> parser) {
            this.value = value;
            this.parser = parser;
        }

        private static ValueType get(int value) {
            for (ValueType type : values()) {
                if (type.value == value) return type;
            }
            throw new IllegalArgumentException("取值异常:" + value);
        }
    }

    public interface ValueChangedListener<T> {
        void onValueChanged(T value, boolean fromUser);
    }

}
